/**
 * 
 */
package qy.jalgotrade.dataseries;

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;

import qy.jalgotrade.utils.PySpecs;

/**
 * <pre>
 * Base class for data series.
 * 
 * .. note::
 *     This is a base class and should not be used directly.
 * </pre>
 * 
 * <pre>
 * It is important to inherit object to get __getitem__ to work properly.
 * Check http://code.activestate.com/lists/python-list/621258/
 * </pre>
 * 
 * @author c-geo
 *
 */
public abstract class DataSeries<T> implements PySpecs.SeqGetItem<T> {

	public enum FillingMode {
		NONE, // 使用 None 填充
		PREVIOUS; // 使用前值填充
	}

	public static final int DEFAULT_MAX_LEN = 4096;

	/**
	 * if maxLen == 0, return DEFAULT_MAX_LEN;
	 * 
	 * @param maxLen
	 * @return
	 * @throws Exception
	 */
	public static int getCheckedMaxLen(int maxLen) throws Exception {

		if (maxLen == 0) {
			maxLen = DEFAULT_MAX_LEN;
		} else if (maxLen < 0) {
			throw new Exception(String.format("Invalid maximum length: %s", maxLen));
		}
		return maxLen;
	}

	/**
	 * 
	 */
	public DataSeries() {

	}

	/**
	 * Returns the number of elements in the data series.
	 * 
	 * @return
	 */
	@Override
	public abstract int __len__();

	/**
	 * Returns the value at a given position/slice. It raises IndexError if the position is invalid, or
	 * TypeError if the key type is invalid.
	 * 
	 * @param idx
	 */
	@Override
	public T __getitem__(int idx) {

		if (idx < 0) {
			idx += __len__();
		}
		if (idx >= __len__() || idx < 0) {
			throw new IndexOutOfBoundsException(String.format("Index out of range: %s", idx));
		}
		return getValueAbsolute(idx);
	}

	/**
	 * This is similar to __getitem__ for ints, but it shouldn't raise for invalid positions.
	 * 
	 * @param pos
	 * @return
	 */
	public abstract T getValueAbsolute(int pos);

	/**
	 * Returns a list of :class:`datetime.datetime` associated with each value.
	 * 
	 * @return
	 */
	public abstract List<ZonedDateTime> getDateTimes();

	/**
	 * 获取指定 datetime 所对应的 index:
	 * 
	 * @param dateTime
	 * @return
	 */
	public int getAbsoluteByDateTime(ZonedDateTime dateTime) {

		return Collections.binarySearch(getDateTimes(), dateTime);
	}

	/**
	 * 根据指定的 datetime, 查找相应的记录并返回:
	 * 
	 * @param dateTime
	 * @return
	 */
	public T getValueByDateTime(ZonedDateTime dateTime) {

		int idx = Collections.binarySearch(getDateTimes(), dateTime);
		return getValueAbsolute(idx);
	}

	/**
	 * <pre>
	 * Appends a value with an associated datetime.
	 * 
	 * .. note::
	 *     If dateTime is not None, it must be greater than the last one.
	 * </pre>
	 * 
	 * @param dateTime
	 * @param value
	 * @throws Exception
	 */
	public abstract void appendWithDateTime(ZonedDateTime dateTime, T value) throws Exception; // TODO: 原方法属于 SequenceDataSeries

	@Override
	public String toString() {

		StringBuilder sb = new StringBuilder("[");
		for (int i = 0; i < __len__(); i++) {
			sb.append("{");
			ZonedDateTime dm = getDateTimes().get(i);
			T val = __getitem__(i);
			if (dm != null) {
				sb.append(dm.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
			} else {
				sb.append("null");
			}
			sb.append(": ").append(val).append("}, ");
		}
		sb = sb.delete(sb.length() - 2, sb.length()).append("]");

		return sb.toString();
	}
}
